//  
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 

using System;

using Gtk;
using Glade;

using Galaxium.Gui;
using Galaxium.Gui.GtkGui;

using L=Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library.Core;
using Galaxium.Protocol.Xmpp.Library.Extensions;

using Anculus.Core;

namespace Galaxium.Protocol.Xmpp.GtkGui
{
	public class BrowseServicesDialog
	{
		[Widget] private Dialog dialog;
		
		[Widget] private Entry host_entry = null;
		[Widget] private TreeView treeview = null;
		[Widget] private ToolButton register_button = null;
	//	[Widget] private ToolButton browse_button = null;
		[Widget] private Button close_button = null;
		[Widget] private Label loading_label = null;

		private L.Client _client;
		private ServiceBrowser _browser;

		public BrowseServicesDialog (L.Client client)
		{
			var gxml = new XML (null, "BrowseServicesDialog.glade", null, null);
			gxml.Autoconnect (this);

			SetupTreeView ();

			_client = client;
			
			host_entry.Activated += HandleEntryActivated;
			host_entry.Text = client.UID.Domain;
			host_entry.Activate ();

			close_button.Clicked += (s, e) => dialog.Destroy ();

			register_button.Clicked += HandleRegisterClicked;

			dialog.DeleteEvent += HandleDeleteEvent;
			
			dialog.ShowAll ();
		}

		void HandleRegisterClicked (object sender, EventArgs e)
		{
			TreeIter iter;
			
			if (treeview.Selection.GetSelected (out iter)) {
				var identity = treeview.Model.GetValue (iter, (int) Columns.Identity) as ServiceIdentity;
				new RegisterDialog (_client, identity.Service.Jid);
			}
		}

		void HandleDeleteEvent(object o, DeleteEventArgs args)
		{
			UnsetBrowser ();
		}

		private enum Columns { ServiceIcon, DisplayText, RegisteredIcon, Identity, Group }
		
		private void SetupTreeView ()
		{
			var col1 = new TreeViewColumn (String.Empty, new CellRendererPixbuf (),
			                               "pixbuf", (int) Columns.ServiceIcon);
			col1.Cells [0].Xalign = 0;
			col1.Cells [0].Xpad = 12;
			var col2 = new TreeViewColumn ();
			col2.PackStart (new CellRendererText (), true);
			col2.PackStart (new CellRendererPixbuf (), false);
			col2.SetAttributes (col2.Cells [0], "markup", (int) Columns.DisplayText);
			col2.SetAttributes (col2.Cells [1], "pixbuf", (int) Columns.RegisteredIcon);
			treeview.AppendColumn (col1);
			treeview.AppendColumn (col2);
			
			var model = new ListStore (typeof (Gdk.Pixbuf),       // ServiceIcon
			                           typeof (String),           // DisplayText
			                           typeof (Gdk.Pixbuf),       // RegisteredIcon
			                           typeof (ServiceIdentity),  // Identity
			                           typeof (String));          // Group
			
			treeview.Model = model;
			treeview.Selection.Mode = SelectionMode.Single;
			treeview.HeadersVisible = false;

			model.DefaultSortFunc = SortFunc;
			model.SetSortColumnId (-1, SortType.Ascending);
			treeview.Selection.Changed += HandleSelectionChanged;

			UnsetBrowser ();
		}

		private int SortFunc (TreeModel model, TreeIter a, TreeIter b)
		{
			var group1 = model.GetValue (a, (int) Columns.Group) as string;
			var group2 = model.GetValue (b, (int) Columns.Group) as string;
			if (group1 == null && group2 != null) return -1;
			if (group1 != null && group2 == null) return 1;
			if (group1 == null && group2 == null) return 0;
			var seq = group1.CompareTo (group2);
			if (seq != 0) return seq;
			var null1 = model.GetValue (a, (int) Columns.Identity) == null;  // is header
			var null2 = model.GetValue (b, (int) Columns.Identity) == null;
			if (null1 && !null2) return -1;
			if (null2 && !null1) return 1;
			var text1 = model.GetValue (a, (int) Columns.DisplayText) as string;
			var text2 = model.GetValue (b, (int) Columns.DisplayText) as string;
			return text1.CompareTo (text2);
		}

		void HandleSelectionChanged (object sender, EventArgs e)
		{
			TreeIter iter;
			if (!treeview.Selection.GetSelected (out iter)) {
				register_button.Sensitive = false;
				return;
			}
			var ident = treeview.Model.GetValue (iter, (int) Columns.Identity) as ServiceIdentity;
			register_button.Sensitive = ident == null ? false : ident.Service.CanRegister;
		}

		private void HandleEntryActivated (object sender, EventArgs e)
		{
			UnsetBrowser ();			
			loading_label.Show ();
			_browser = new ServiceBrowser (_client, host_entry.Text);
			_browser.ServiceReceived += HandleServiceReceived;
			_browser.Finished += HandleFinished;
			_browser.ErrorOccured += HandleErrorOccured;
			_browser.Request ();
		}

		private void UnsetBrowser ()
		{
			var model = treeview.Model as ListStore;
			model.Clear ();
			model.AppendValues (null, String.Empty, null, null, "gateways");
			model.AppendValues (null, String.Empty, null, null, "other");
			model.AppendValues (null, "<big><b>Gateways</b></big>", null, null, "gateways");
			model.AppendValues (null, "<big><b>Conferences</b></big>", null, null, "conferences");
			model.AppendValues (null, "<big><b>Other</b></big>", null, null, "other");
			if (_browser == null) return;
			_browser.ServiceReceived -= HandleServiceReceived;
			_browser.Finished -= HandleFinished;
			_browser.ErrorOccured -= HandleErrorOccured;
		}

		void HandleErrorOccured (object sender, StanzaErrorEventArgs e)
		{
			Application.Invoke ((s,a) => loading_label.Hide ());
			// TODO: display error
		}

		void HandleFinished (object sender, EventArgs e)
		{
			Application.Invoke ((s,a) => loading_label.Hide ());
		}

		void HandleServiceReceived (object sender, ServiceEventArgs e)
		{
			Application.Invoke (delegate {
				var reg_icon = e.Service.IsRegistered ?
					IconUtility.GetIcon ("service-registered", IconSizes.Medium) : null;
				
				foreach (var identity in e.Service.Identities) {

					var icon = IconUtility.GetIcon ("service-" + identity.Category
					                                + "_" + identity.Type, IconSizes.Medium);
					if (icon == null)
						icon = IconUtility.GetIcon ("service-" + identity.Category, IconSizes.Medium);

					if (icon == null)
						icon = IconUtility.GetIcon ("service-unknown", IconSizes.Medium);

					var text = "  <b>" + GLib.Markup.EscapeText (identity.Name) + "</b>";
					text += "\n  " + GLib.Markup.EscapeText (identity.Service.Jid);
					if (identity.Service.Node != null)
						text += ", node " + GLib.Markup.EscapeText (identity.Service.Node);
					text += " (" + identity.Category + "/" + identity.Type + ")";

					string group = null;
					switch (identity.Category) {
						case "gateway":    group = "gateways";    break;
						case "conference": group = "conferences"; break;
						default:           group = "other";       break;
					}
					
					var model = treeview.Model as ListStore;
					model.AppendValues (icon, text, reg_icon, identity, group);
				}
			});
		}
	}
}
